package edu.mse.camel.client.whiteboard.gef.ui.editpart;

import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PolygonDecoration;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractConnectionEditPart;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.editpolicies.ConnectionEditPolicy;
import org.eclipse.gef.editpolicies.ConnectionEndpointEditPolicy;
import org.eclipse.gef.requests.GroupRequest;
import org.eclipse.ui.views.properties.IPropertySource;

import edu.mse.camel.client.whiteboard.gef.ui.WhiteBoardUtil;
import edu.mse.camel.client.whiteboard.gef.ui.commands.ConnectionDeleteCommand;
import edu.mse.camel.client.whiteboard.gef.ui.properties.ConnectionPropertySource;
import edu.mse.camel.client.whiteboard.gef.whiteboard.Connection;
import edu.mse.camel.client.whiteboard.gef.whiteboard.WhiteboardPackage;

/**
 * Edit part for Connection model elements.
 * <p>This edit part must implement the PropertyChangeListener interface, 
 * so it can be notified of property changes in the corresponding model element.
 * </p>
 * @author darpan
 */
class ConnectionEditPart extends AbstractConnectionEditPart implements IWhiteboardEditPart {
	
	public AbstractGraphicalEditPart getEditPart() {
		return this;
	}
	
	public ConnectionEditPart(Object model) {
		this.setModel(model);
	}
	
	private ConnectionAdapter adapter = new ConnectionAdapter(this);
	
	private class ConnectionAdapter implements Adapter {
		
		private Notifier target;
		private ConnectionEditPart editPart;
		
		public ConnectionAdapter(ConnectionEditPart part) {
			this.editPart = part;
		}
		
		/**
		 * @see org.eclipse.emf.common.notify.Adapter#notifyChanged(org.eclipse.emf.common.notify.Notification)
		 */
		public void notifyChanged(Notification notification) {
			int type = notification.getEventType();
			int featureId = notification.getFeatureID(WhiteboardPackage.class);
			if(type == Notification.SET) {
				switch(featureId) {
				case WhiteboardPackage.CONNECTION__DASHED:
					if(getCastedModel().isDashed()) {
						((PolylineConnection) getFigure()).setLineStyle(Graphics.LINE_DASH);
					}
					else {
						((PolylineConnection) getFigure()).setLineStyle(Graphics.LINE_SOLID);
					}
					refreshVisuals();
					break;
				}
			}
		}
		
		/**
		 * @see org.eclipse.emf.common.notify.Adapter#setTarget(org.eclipse.emf.common.notify.Notifier)
		 */
		public void setTarget(Notifier newTarget) {
			target = newTarget;
		}
		
		/**
		 * @see org.eclipse.emf.common.notify.Adapter#isAdapterForType(java.lang.Object)
		 */
		public boolean isAdapterForType(Object type) {
			return (getModel().getClass() == type);
		}
		
		/**
		 * @see org.eclipse.emf.common.notify.Adapter#getTarget()
		 */
		public Notifier getTarget() {
			return target;
		}
		
	}
	
	/**
	 * Upon activation, attach to the model element as a property change listener.
	 */
	public void activate() {
		if (!isActive()) {
			hookIntoModel(getCastedModel());
			super.activate();
		}
	}
	
	private void hookIntoModel(EObject model) {
		if(model != null) {
			model.eAdapters().add(adapter);
		}
	}
	
	private void unhookFromModel(EObject model) {
		if(model != null) {
			model.eAdapters().remove(adapter);
		}
	}
	
	public Object getAdapter(Class key) {
		if (IPropertySource.class == key)
			return new ConnectionPropertySource(getModel());
		return super.getAdapter(key);
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
	 */
	protected void createEditPolicies() {
		// Selection handle edit policy. 
		// Makes the connection show a feedback, when selected by the user.
		installEditPolicy(EditPolicy.CONNECTION_ENDPOINTS_ROLE,
				new ConnectionEndpointEditPolicy());
		// Allows the removal of the connection model element
		installEditPolicy(EditPolicy.CONNECTION_ROLE, new ConnectionEditPolicy() {
			protected Command getDeleteCommand(GroupRequest request) {
				return new ConnectionDeleteCommand(getCastedModel());
			}
		});
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
	 */
	protected IFigure createFigure() {
		PolylineConnection connection = (PolylineConnection) super.createFigure();
		connection.setTargetDecoration(new PolygonDecoration()); // arrow at target endpoint
		// this check is kind of stupid, I really should be using an enumerated
		// type for the line style that matches Graphics :/
		if(getCastedModel().isDashed()) {
			connection.setLineStyle(Graphics.LINE_DASH);
		}
		else {
			connection.setLineStyle(Graphics.LINE_SOLID);
		}
		connection.setForegroundColor(WhiteBoardUtil.createColorFromRGB(getCastedModel().getColor()));
		return connection;
	}
	
	/**
	 * Upon deactivation, detach from the model element as a property change listener.
	 */
	public void deactivate() {
		if (isActive()) {
			unhookFromModel(getCastedModel());
			super.deactivate();
		}
	}
	
	private Connection getCastedModel() {
		return (Connection) getModel();
	}
	
}